package com.ext.aic.box.wx.conversation;

import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;

import com.ext.aic.AICApplication;
import com.ext.aic.AICManager;
import com.ext.aic.beans.box.ConversationViewElement;
import com.ext.aic.beans.box.ConversationWindowElement;
import com.ext.aic.beans.box.NotificationMessage;
import com.ext.aic.beans.box.ViewElement;
import com.ext.aic.box.TripartiteLoginStatus;
import com.ext.aic.box.enums.Direction;
import com.ext.aic.box.model.ClientUIConstants;
import com.ext.aic.box.model.ReferenceNodeFence;
import com.ext.aic.box.wx.WxDbHelper;
import com.ext.aic.box.wx.WxViews;
import com.ext.aic.box.wx.chat.ChatManager;
import com.ext.aic.constants.BoxConstants;
import com.ext.aic.constants.CacheConstants;
import com.ext.aic.enums.ChatSource;
import com.ext.aic.events.OnAccessibilityServiceEvent;
import com.ext.aic.handler.AccessibilityClickHandler;
import com.ext.aic.utils.AccessibilityNodeUtils;
import com.ext.aic.utils.BoxUtils;
import com.ext.aic.utils.WhiteListUtils;
import com.ext.aic.utils.WxElementUtils;
import com.eyinfo.android_pure_utils.ObjectManager;
import com.eyinfo.android_pure_utils.events.Action1;
import com.eyinfo.android_pure_utils.executor.CustomThreadPool;
import com.eyinfo.android_pure_utils.utils.ObjectJudge;
import com.eyinfo.android_pure_utils.utils.TaskUtils;
import com.eyinfo.mpkv.MPLocalKV;

import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import lombok.Getter;
import lombok.Setter;

public class ConversationManager {

    private WxDbHelper dbHelper = new WxDbHelper();
    private WxViews wxViews = new WxViews();
    private CustomThreadPool threadPool = new CustomThreadPool();
    @Getter
    private boolean detecting;
    @Setter
    @Getter
    private boolean rolling;
    private AccessibilityNodeInfo scrollNode;
    private int rowIndex = 0;
    private int computeItemY = 0; //计算item Y坐标
    @Getter
    private long previousAnalyzeTime;//前一次解析时间
    @Setter
    private boolean isMatchedMessage = false;
    //修证移动计数
    private AtomicInteger repairMoveCount = new AtomicInteger(0);
    //前一次匹配时间
    private long preMatchTime = 0;

    //region 如果有通知则禁用解析，走通知渠道查找
    public boolean isProcessNotifyAnalyze() {
        boolean isProcessedNotify = MPLocalKV.getInstance().getBoolean(CacheConstants.PROCESSED_NOTIFY_STATUS_KEY, true);
        if (isProcessedNotify) {
            MPLocalKV.getInstance().put(CacheConstants.PROCESSED_NOTIFY_STATUS_KEY, false);
            return false;
        }
        return true;
    }

    private boolean isCancelAnalyze() {
        boolean prePcLoginStatus = TripartiteLoginStatus.getInstance().getWeChatPrePcLoginStatus();
        boolean loginPcWeChat = TripartiteLoginStatus.getInstance().isLoginPcWeChat();
        if (prePcLoginStatus != loginPcWeChat) {
            TripartiteLoginStatus.getInstance().saveWeChatPrePcLoginStatus(loginPcWeChat);
            return true;
        }
        return false;
    }
    //endregion

    //region 是否存在会话列表
    private boolean isExistConversationList(ConversationWindowElement conversationWindowElement) {
        if (conversationWindowElement == null) {
            return false;
        }
        String windowId = conversationWindowElement.getWindowId();
        String listId = conversationWindowElement.getListId();
        return !TextUtils.isEmpty(windowId) && !TextUtils.isEmpty(listId);
    }
    //endregion

    //region 解析会话列表
    public void analyzeConversationList(ConversationWindowElement conversationWindowElement, AccessibilityNodeInfo rootNode) {
        if (isMatchedMessage) {
            long diff = System.currentTimeMillis() - preMatchTime;
            if (diff <= 2000) {
                return;
            }
            isMatchedMessage = false;
        }
        if (!isExistConversationList(conversationWindowElement)) {
            detecting = false;
            previousAnalyzeTime = System.currentTimeMillis();
            return;
        }
        detecting = true;
        rowIndex = 0;
        String listId = conversationWindowElement.getListId();
        scrollNode = AccessibilityNodeUtils.getInstance().findNode(rootNode, listId, BoxConstants.listClassName, resourceId -> {
            updateConversationId(resourceId, conversationWindowElement);
        });
        if (scrollNode == null) {
            detecting = false;
            rowIndex = 0;
            previousAnalyzeTime = System.currentTimeMillis();
            return;
        }
        SystemClock.sleep(15);
        scrollToTop(scrollNode);
        isMatchedMessage = false;
        ConversationViewElement conversationWindowElements = WxElementUtils.getInstance().getConversationWindowElements();
        List<ViewElement> conversationListItemElements = conversationWindowElements.getListItemIds();
        recognitionConversationItems(conversationListItemElements, true);
    }

    private void forwardScrollToPosition(AccessibilityNodeInfo scrollableNode) {
        rolling = true;
        List<AccessibilityNodeInfo.AccessibilityAction> actionList = scrollableNode.getActionList();
        for (AccessibilityNodeInfo.AccessibilityAction action : actionList) {
            if (action.getId() == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION.getId()) {
                Bundle args = new Bundle();
                args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_ROW_INT, rowIndex);
                scrollableNode.performAction(action.getId(), args);
                break;
            }
        }
    }

    private void scrollToTop(AccessibilityNodeInfo scrollableNode) {
        rolling = true;
        AccessibilityNodeInfo.AccessibilityAction action = AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION;
        if (scrollableNode.getActionList().contains(action)) {
            Bundle args = new Bundle();
            args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_ROW_INT, 0);
            scrollableNode.performAction(action.getId(), args);
        }
    }

    //是否修复第一项位置

    private void recognitionConversationItems(List<ViewElement> conversationListItemElements, boolean isFirst) {
        List<AccessibilityNodeInfo> itemNodes = AccessibilityNodeUtils.getInstance().findNodesById(scrollNode, conversationListItemElements);
        if (ObjectJudge.isNullOrEmpty(itemNodes)) {
            if (isFirst) {
                detecting = false;
                rowIndex = 0;
                previousAnalyzeTime = System.currentTimeMillis();
            }
            return;
        }
        int size = itemNodes.size();
        if (size <= 2) {
            detecting = false;
            rowIndex = 0;
            previousAnalyzeTime = System.currentTimeMillis();
            return;
        }
        OnAccessibilityServiceEvent serviceEvent = BoxUtils.getInstance().getServiceEvent();
        ClientUIConstants clientUIConstants = serviceEvent.getClientUIConstants();
        boolean lastPage = isLastPage(itemNodes.get(size - 1));
        int height = clientUIConstants.getConversationItemHeight();
        int realHeight = height * size;
        int lastOffsetY = clientUIConstants.getScreenHeight() - realHeight - clientUIConstants.getTabBarHeight();
        computeItemY = getFirstItemY(isFirst, clientUIConstants);

        for (int i = 0; i < size; i++) {
            AccessibilityNodeInfo nodeInfo = itemNodes.get(i);
            if (i == 0) {
                if (!lastPage && (lastOffsetY < height) && !hasNewMessages(nodeInfo) && repairMoveCount.get() == 0) {
                    repairMoveCount.addAndGet(1);
                    forwardScrollToPosition(scrollNode);
                    return;
                } else {
                    repairMoveCount.set(0);
                }
            }
            if (isProcessMessage(nodeInfo)) {
                isMatchedMessage = true;
                preMatchTime = System.currentTimeMillis();
                Rect currentRect = getCurrentRect(itemNodes, i, height, clientUIConstants, lastPage, isFirst);
                if (currentRect.isEmpty()) {
                    isMatchedMessage = false;
                    continue;
                }
                Long screenshotId = generateItemScreenshot(currentRect);
                ConversationRecognize conversationRecognize = new ConversationRecognize();
                conversationRecognize.singleRecognize(screenshotId, new Action1<NotificationMessage>() {
                    @Override
                    public void call(NotificationMessage notificationMessage) {
                        isMatchedMessage = false;
                        if (notificationMessage == null) {
                            recognitionConversationItems(conversationListItemElements, isFirst);
                            return;
                        }
                        if (notificationMessage.getFinish() == 1) {
                            return;
                        }
                        if (!isWhiteListUser(notificationMessage.getUser())) {
                            recognitionConversationItems(conversationListItemElements, isFirst);
                            return;
                        }
                        ChatManager chatManager = serviceEvent.getChatManager();
                        chatManager.setNotificationMessage(notificationMessage);
                        chatManager.setParams(true, ChatSource.CONVERSATION);

                        float x = currentRect.right / 2f;
                        float y = currentRect.top + height / 2f;

                        AccessibilityClickHandler clickHandler = new AccessibilityClickHandler();
                        clickHandler.clickAtPoint(serviceEvent.getService(), x, y);
                    }
                });
                break;
            }
            rowIndex++;
        }
        computeItemY = 0;
        repairMoveCount.set(0);
        if (lastPage || isMatchedMessage) {
            rowIndex = 0;
            previousAnalyzeTime = System.currentTimeMillis();
            detecting = false;
            serviceEvent.getConversationRecognize().startRecognizeDetection();
        } else {
            forwardScrollToPosition(scrollNode);
        }
    }

    private boolean isProcessMessage(AccessibilityNodeInfo nodeInfo) {
        if (isMatchedMessage) {
            return false;
        }
        return hasNewMessages(nodeInfo);
    }

    private Rect getCurrentRect(List<AccessibilityNodeInfo> itemNodes, int position, int itemHeight, ClientUIConstants clientUIConstants, boolean lastPage, boolean isFirst) {
        int firstItemY = getFirstItemY(true, clientUIConstants);

        Rect rect = new Rect();
        rect.left = 0;
        rect.right = clientUIConstants.getScreenWidth();
        rect.top = itemHeight * position + firstItemY;
        rect.bottom = rect.top + itemHeight;
        positionCorrection(itemNodes, position, rect, lastPage, isFirst, clientUIConstants);
        return rect;
    }

    private boolean hasNewMessages(AccessibilityNodeInfo itemNode) {
        String newMessageMarkId = wxViews.getNewMessageMarkId();
        if (TextUtils.isEmpty(newMessageMarkId)) {
            return false;
        }
        List<AccessibilityNodeInfo> nodes = itemNode.findAccessibilityNodeInfosByViewId(newMessageMarkId);
        return !ObjectJudge.isNullOrEmpty(nodes);
    }

    private Rect getItemRect(AccessibilityNodeInfo itemNode) {
        Rect rect = new Rect();
        itemNode.getBoundsInScreen(rect);
        return rect;
    }

    /**
     * 匹配参考节点,最多匹配前后各N位，找到匹配为止直到列表队首或队尾
     * 当有一个方向未匹配到转到反方向匹配, 直到找到为止
     *
     * @param itemNodes 原节点集合
     * @param position  当前节点位置
     * @return 参考节点围栏信息
     */
    private ReferenceNodeFence findReferenceNode(List<AccessibilityNodeInfo> itemNodes, int position, Direction original, Direction present) {
        ReferenceNodeFence fence = new ReferenceNodeFence();
        int size = itemNodes.size();
        Rect current = getItemRect(itemNodes.get(position));
        if (!current.isEmpty()) {
            fence.setBounds(current);
            fence.setDirection(present);
            fence.setPosition(position);
            return fence;
        }
        if (Direction.up == original) {
            if (Direction.up == present) {
                if ((position - 1) == 2) {
                    return findReferenceNode(itemNodes, position - 1, original, Direction.down);
                } else if ((position - 1) > 2) {
                    return findReferenceNode(itemNodes, position - 1, original, Direction.up);
                }
            } else if ((position + 1) < size) {
                return findReferenceNode(itemNodes, position + 1, original, Direction.down);
            }
        } else if (original == Direction.down) {
            if (present == Direction.down) {
                if ((position + 1) == (size - 1)) {
                    return findReferenceNode(itemNodes, position + 1, original, Direction.up);
                } else if ((position + 1) < (size - 1)) {
                    return findReferenceNode(itemNodes, position + 1, original, Direction.down);
                }
            } else if ((position - 1) > 2) {
                return findReferenceNode(itemNodes, position - 1, original, Direction.up);
            }
        }
        return null;
    }

    //对bounds位置进行进一步修证
    private void positionCorrection(List<AccessibilityNodeInfo> itemNodes, int position, Rect rect, boolean lastPage, boolean isFirst, ClientUIConstants clientUIConstants) {
        Direction direction = lastPage ? Direction.down : Direction.up;
        ReferenceNodeFence referenceNode = findReferenceNode(itemNodes, position, direction, direction);
        if (referenceNode == null || referenceNode.getBounds() == null || referenceNode.getBounds().isEmpty()) {
            return;
        }
        int statusHeight = clientUIConstants.getStatusHeight();
        int headerHeight = clientUIConstants.getHeaderHeight();
        int tabBarHeight = clientUIConstants.getTabBarHeight();
        int screenHeight = clientUIConstants.getScreenHeight();

        direction = referenceNode.getDirection();
        Rect bounds = referenceNode.getBounds();
        if (bounds.isEmpty()) {
            return;
        }
        if (Direction.up == direction) {
            int count = Math.abs(position - referenceNode.getPosition());
            rect.top = bounds.top + clientUIConstants.getConversationItemHeight() * count;
            rect.bottom = bounds.bottom + clientUIConstants.getConversationItemHeight() * count;
        } else if (Direction.down == direction) {
            int count = Math.abs(referenceNode.getPosition() - position);
            rect.top = bounds.top - clientUIConstants.getConversationItemHeight() * count;
            rect.bottom = bounds.bottom - clientUIConstants.getConversationItemHeight() * count;
        }
        int topOffset = Math.abs(rect.bottom - getFirstItemY(isFirst, clientUIConstants));
        int bottomOffset = Math.abs(screenHeight - tabBarHeight - rect.top);
        if (topOffset < clientUIConstants.getConversationItemHeight() || bottomOffset < clientUIConstants.getConversationItemHeight()) {
            return;
        } else {
            computeItemY = rect.top;
        }
    }

    private int getFirstItemY(boolean isFirst, ClientUIConstants clientUIConstants) {
        if (isFirst && TripartiteLoginStatus.getInstance().isLoginPcWeChat()) {
            return clientUIConstants.getStatusHeight() + clientUIConstants.getLoginStatusHeight() + clientUIConstants.getHeaderHeight();
        } else {
            return clientUIConstants.getStatusHeight() + clientUIConstants.getHeaderHeight();
        }
    }

    private boolean isLastPage(AccessibilityNodeInfo lastNode) {
        Rect rect = new Rect();
        lastNode.getBoundsInScreen(rect);
        if (rect.top == 0 && rect.bottom == 0) {
            return false;
        }
        Context applicationContext = AICApplication.getInstance().getApplicationContext();
        int screenHeight = ObjectManager.getScreenHeight(applicationContext);
        int diff = Math.abs(rect.bottom - screenHeight);
        if (diff >= 58) {
            return true;
        }
        return diff < 10;
    }

    private Long generateItemScreenshot(Rect rect) {
        AtomicLong conversationIdAtomic = new AtomicLong(0);
        CountDownLatch latch = new CountDownLatch(1);
        TaskUtils.getInstance().execute(new ConversationScreenshot(rect, new Action1<Long>() {
            @Override
            public void call(Long id) {
                Log.d("conversation", "【9】释放锁");
                conversationIdAtomic.addAndGet(id == null ? 0 : id);
                latch.countDown();
            }
        }));
        try {
            boolean await = latch.await(2500, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            latch.countDown();
            Thread.currentThread().interrupt();
        }
        return conversationIdAtomic.get();
    }

    public void recognitionNextConversation() {
        ConversationViewElement conversationWindowElements = WxElementUtils.getInstance().getConversationWindowElements();
        List<ViewElement> conversationListItemElements = conversationWindowElements.getListItemIds();
        recognitionConversationItems(conversationListItemElements, false);
    }

    private void updateConversationId(String resourceId, ConversationWindowElement windowElement) {
        windowElement.setListId(resourceId);
        AICManager.getInstance().insertOrUpdate(ConversationWindowElement.class, windowElement);
    }
    //endregion

    //region 是否白名单用户
    private boolean isWhiteListUser(String currentUser) {
        Set<String> whiteListUsers = WhiteListUtils.getInstance().getWhiteListUsers();
        return whiteListUsers.contains(currentUser);
    }
    //endregion
}
